package com.wolfking.mask;

import com.google.common.collect.ImmutableMap;
import com.google.gson.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Slf4j
public class DataMaskUtils {

    public static final String NAME = "name";
    public static final String MOBILE = "mobile";
    public static final String ID_CARD = "id_card";
    public static final String BANK_CARD = "bank_card";

    private static final Gson FORMAT = new GsonBuilder().setPrettyPrinting().create();

    // 姓名脱敏
    public static String maskName(final String initName) {
        if (StringUtils.isBlank(initName)) {
            return initName;
        }
        int length = initName.trim().length();

        String last = StringUtils.substring(initName.trim(), length - 1, length);

        if (length == 2) {
            return "*" + last;
        } else if (length >= 3) {
            return "**" + last;
        }
        return initName;
    }

    // 身份证号脱敏
    public static String maskIdCard(final String initIdCard) {
        if (StringUtils.isBlank(initIdCard)) {
            return initIdCard;
        }
        int len = initIdCard.length();

        if (len <= 10) {
            return initIdCard;
        }
        return initIdCard.substring(0, 6) + StringUtils.repeat("*", len - 10) + initIdCard.substring(len - 4);
    }

    // 手机号脱敏
    public static String maskMobile(final String initMobile) {
        if (StringUtils.isBlank(initMobile)) {
            return initMobile;
        }
        if (initMobile.length() != 11) {
            return initMobile;
        }
        return initMobile.trim().substring(0, 3) + "****" + initMobile.trim().substring(7);
    }

    // 银行卡号脱敏
    public static String maskBankCard(String initBankCard) {
        if (StringUtils.isBlank(initBankCard)) {
            return initBankCard;
        }
        int len = initBankCard.length();

        if (len <= 8) {
            return initBankCard;
        }
        return initBankCard.substring(0, 4) + StringUtils.repeat("*", len - 8) + initBankCard.substring(len - 4);
    }

    /**
     * 获取脱敏卡号的前缀和后缀
     *
     * @param bankCard 脱敏卡号
     * @return
     */
    public static String[] getBankCardPrefixSuffix(String bankCard) {
        List<String> list = Arrays.stream(bankCard.split("\\*")).filter(StringUtils::isNotBlank)
                .collect(Collectors.toList());
        return list.size() == 2 ? list.toArray(new String[2]) : new String[]{"aaaa", "bbbbb"};
    }

    /**
     * @param beforeMask 原来的json数据
     * @param map        脱敏配置
     * @param format     是否返回格式化json
     * @return
     */
    public static String getMaskString(String beforeMask, Map<String, String> map, boolean format) {
        try {
            JsonParser jsonParser = new JsonParser();
            JsonElement element = jsonParser.parse(beforeMask);
            if (element.isJsonObject()) {
                JsonObject object = element.getAsJsonObject();
                for (Map.Entry<String, String> entry : map.entrySet()) {
                    maskElement(object, entry.getKey(), entry.getValue());
                }
            } else if (element.isJsonArray()) {
                JsonArray array = element.getAsJsonArray();
                for (JsonElement ele : array) {
                    for (Map.Entry<String, String> entry : map.entrySet()) {
                        maskElement(ele.getAsJsonObject(), entry.getKey(), entry.getValue());
                    }
                }
            }
            if (format) {
                return FORMAT.toJson(element);
            } else {
                return element.toString();
            }
        } catch (Throwable e) {
            log.error("mask error", e);
            return beforeMask;
        }
    }

    private static void maskElement(JsonElement element, String path, String type) {
        if (element != null) {
            String[] paths = path.split("\\.");
            if (paths.length == 1) {
                if (element.isJsonObject()) {
                    JsonObject asJsonObject = element.getAsJsonObject();
                    JsonElement asJsonElement = asJsonObject.get(path);
                    if (asJsonElement == null || asJsonElement.isJsonNull()) {
                        return;
                    }
                    JsonPrimitive primitive = asJsonObject.getAsJsonPrimitive(path);
                    if (primitive != null) {
                        String asString = primitive.getAsString();
                        String newValue = asString;
                        if (StringUtils.isNotBlank(asString)) {
                            try {
                                switch (type) {
                                    case NAME:
                                        newValue = maskName(asString);
                                        break;
                                    case ID_CARD:
                                        newValue = maskIdCard(asString);
                                        break;
                                    case MOBILE:
                                        newValue = maskMobile(asString);
                                        break;
                                    case BANK_CARD:
                                        newValue = maskBankCard(asString);
                                        break;
                                    default:
                                        log.error("un know type {}", type);
                                        break;
                                }
                            } catch (Exception e) {
                                log.error("mask error", e);
                            }
                            if (!asString.equals(newValue)) {
                                for (Map.Entry<String, JsonElement> entry : asJsonObject.entrySet()) {
                                    if (entry.getKey().equals(path)) {
                                        entry.setValue(new JsonPrimitive(newValue));
                                    }
                                }
                            }
                        }
                    }
                } else if (element.isJsonArray()) {
                    JsonArray array = element.getAsJsonArray();
                    for (JsonElement ele : array) {
                        maskElement(ele.getAsJsonObject(), path, type);
                    }
                }
            } else {
                Optional<String> reduce = Stream.of(paths).skip(1).reduce((e1, e2) -> (e1 + "." + e2));
                if (reduce.isPresent()) {
                    if (element.isJsonObject()) {
                        JsonObject asJsonObject = element.getAsJsonObject();
                        maskElement(asJsonObject.get(paths[0]), reduce.get(), type);
                    } else if (element.isJsonArray()) {
                        JsonArray asJsonArray = element.getAsJsonArray();
                        for (JsonElement jsonElement : asJsonArray) {
                            maskElement(jsonElement.getAsJsonObject().get(paths[0]), reduce.get(), type);
                        }
                    }
                }
            }
        }
    }

    public static void main(String[] args) {
        String json = "[\n" +
                "\t{\n" +
                "\t\t\"name\": \"西门吹雪\",\n" +
                "\t\t\"idCard\": 395812198902253698,\n" +
                "\t\t\"age\": 100,\n" +
                "\t\t\"businessOrder\": [\n" +
                "\t\t\t{\n" +
                "\t\t\t\t\"businessOrderId\": \"456\",\n" +
                "\t\t\t\t\"bankCard\": 62251345678903656,\n" +
                "\t\t\t\t\"mobile\": \"13769382536\"\n" +
                "\t\t\t}\n" +
                "\t\t]\n" +
                "\t},\n" +
                "\t{\n" +
                "\t\t\"name\": \"张飞\",\n" +
                "\t\t\"idCard\": 395812198902253696,\n" +
                "\t\t\"extraRechargeAmount\": \"29.89\",\n" +
                "\t\t\"businessOrder\": {\n" +
                "\t\t\t\"businessOrderId\": \"123\",\n" +
                "\t\t\t\"bankCard\": 62251345678903652,\n" +
                "\t\t\t\"mobile\": \"13769382537\"\n" +
                "\t\t}\n" +
                "\t}\n" +
                "]";
        Map<String, String> map = ImmutableMap.of("name", NAME, "idCard", ID_CARD,
                "businessOrder.bankCard", BANK_CARD, "businessOrder.mobile", MOBILE);
        String maskString = getMaskString(json, map, true);
        System.out.println(maskString);
    }

}
